
import java.io.File;
import java.io.IOException;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.export.binary.BinaryImporter;
import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;


public class World {
	
	public Node worldNode;
	static Chunk[] activeChunks;
	static int[][] chunkPosition;
	int numActiveChunks, drawDistance = 10;
	float spaceConstant = .0f;
	public static String CHUNK_DIR;
	int worldID;
	int numChunks = 0;
	int nextChunkID = 0;
	/*
	 * drawDistance: chunks are generated in a square around the player.  Each increment in the number of chunks between the player
	 * 				 and the edge of the world increases the number of chunks that need to be drawn by (drawDistance*2 - 1)^2. 
	 * 				 
	 */
	public World()
	{
		worldID = 0;
		CHUNK_DIR = Engine.GAME_HOME + "/world" + worldID + "/chunks";
		numActiveChunks = (int) Math.pow(drawDistance*2 - 1, 2);
		activeChunks = new Chunk[numActiveChunks];
		chunkPosition = new int[numActiveChunks][3];
		
		worldNode = new Node();
		Engine.getEngine().getRootNode().attachChild(worldNode);
	}
	
	
	/*
	 * Draw starting chunk and then draw additional chunks in a spiral around it.
	 * figure out how to calculate how many elements are in each row for a given drawLevel
	 */
	public void createNewWorld()
	{
		nextChunkID = 0;
		int chunkNumForDrawLevel = 0;//chunk number for this level
		int totalChunkNum = 0;//total chunk number
		Chunk previousChunk = null;
		
		for(int drawLevel = 0; drawLevel < drawDistance; drawLevel++)
		{
			if(drawLevel == 0)//special case for starting chunk
			{
				activeChunks[totalChunkNum] = new Chunk(nextChunkID, 0, 0);
				previousChunk = activeChunks[totalChunkNum];
				totalChunkNum++;
			}
			else
			{
				
				//this generates north row
				for(int i = 0; i < drawLevel + 1; i++)
				{
					if(chunkNumForDrawLevel == 0)
					{
						activeChunks[totalChunkNum] = new Chunk(nextChunkID, 0, Chunk.depth * drawLevel);
						previousChunk = activeChunks[totalChunkNum];
						totalChunkNum++;
						chunkNumForDrawLevel++;
					}
					else
					{
						activeChunks[totalChunkNum] = new Chunk(nextChunkID, previousChunk.offsetX + Chunk.width, previousChunk.offsetZ);
						previousChunk = activeChunks[totalChunkNum];
						totalChunkNum++;
						chunkNumForDrawLevel++;
					}
				}
				
				//this generates east row
				for(int i = 0; i < drawLevel * 2; i++)
				{
					activeChunks[totalChunkNum] = new Chunk(nextChunkID, previousChunk.offsetX, previousChunk.offsetZ - Chunk.depth);
					previousChunk = activeChunks[totalChunkNum];
					totalChunkNum++;
					chunkNumForDrawLevel++;
				}
				
				//this generates south row
				for(int i = 0; i < drawLevel * 2; i++)
				{
					activeChunks[totalChunkNum] = new Chunk(nextChunkID, previousChunk.offsetX - Chunk.width, previousChunk.offsetZ);
					previousChunk = activeChunks[totalChunkNum];
					totalChunkNum++;
					chunkNumForDrawLevel++;
				}
				//this generates west row
				for(int i = 0; i < drawLevel * 2; i++)
				{
					activeChunks[totalChunkNum] = new Chunk(nextChunkID, previousChunk.offsetX, previousChunk.offsetZ + Chunk.depth);
					previousChunk = activeChunks[totalChunkNum];
					totalChunkNum++;
					chunkNumForDrawLevel++;
				}
				
				//this generates the other half of the north row
				for(int i = 0; i < drawLevel * 2 - drawLevel - 1; i++)
				{
					activeChunks[totalChunkNum] = new Chunk(nextChunkID, previousChunk.offsetX - Chunk.width, previousChunk.offsetZ);
					previousChunk = activeChunks[totalChunkNum];
					totalChunkNum++;
					chunkNumForDrawLevel++;
				}
			}
			chunkNumForDrawLevel = 0;
		}
	}
	
	public static Chunk getChunk(int x, int z)
	{
		for(int i = 0; i < activeChunks.length; i++)
		{
			if(activeChunks[i].offsetX == x && activeChunks[i].offsetZ == z)
			{
				return activeChunks[i];
			}
		}
		return null;
	}
	
	//pretty primitive. come up with something better.
	public static boolean chunkExists(int x, int z)
	{
		for(int i = 0; i < activeChunks.length; i++)
		{
			if(activeChunks[i].offsetX == x && activeChunks[i].offsetZ == z)
			{
				return true;
			}
		}
		return false;
	}
	
	public Chunk getChunk(String index)
	{
		String sx = "";
		String sz = "";
		int i;
		for(i = 0; i < index.length(); i++)
		{
			if(index.charAt(i) == '^')
				break;
			else
				sx += index.charAt(i);
		}
		for(i++; i < index.length(); i++)
		{
			sz += index.charAt(i);
		}
		int x = Integer.parseInt(sx);
		int z = Integer.parseInt(sz);
		for(i = 0; i < activeChunks.length; i++)
		{
			if(x == activeChunks[i].offsetX && z == activeChunks[i].offsetZ)
				return activeChunks[i];
		}
		
		return null;
	}
	
	public void saveChunk(Chunk c)
	{
		
		new File(CHUNK_DIR).mkdir();
		BinaryExporter exporter = BinaryExporter.getInstance();
		
		File saveChunk = new File(CHUNK_DIR + "/" + "chunk"+ c.offsetX + "-" + c.offsetZ + ".j3o");
		
		try
		{
			exporter.save(c, saveChunk);
		}
		catch(IOException ex)
		{
			//Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error saving chunk" + c.chunkID, ex);
			System.out.println("Error saving chunk");
		}
	}
	
	public void saveActiveChunks()
	{
		for(int i = 0; i < activeChunks.length; i++)
		{
			saveChunk(activeChunks[i]);
		}
	}
	
	public Chunk loadChunk()
	{
		BinaryImporter importer = BinaryImporter.getInstance();
		importer.setAssetManager(Engine.getEngine().getAssetManager());
		File f = new File(CHUNK_DIR + "/chunk0.j3o");
		Chunk c = null;
		
		try
		{
			c = (Chunk)importer.load(f);
		}
		catch(IOException ex)
		{
			//Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error loading chunk", ex);
			System.out.println("Error loading chunk");
		}
		return c;
	}
	
	public Node getWorldNode()
	{
		return worldNode;
	}
}
